Go 自定义 Json 序列化规则 您所在的位置:网站首页 golang 自定义json masharl Go 自定义 Json 序列化规则

Go 自定义 Json 序列化规则

2024-06-17 04:32| 来源: 网络整理| 查看: 265

开发过程中,我们经常会使用 JSON 作为数据传输格式。而这离不开对 JSON 数据的编解码工作,在 Go 中,encoding/json 包提供了这些能力。

我们可以使用 encoding/json 包的 Encoder.Encode() 和 Marshal() 实现 Json 序列化,使用 Decoder.Decode() 和 Unmarshal() 实现 Json 反序列化。

示例如下

package main import (  "encoding/json"  "os" ) type Metric struct {  Name  string `json:"name"`  Value int64  `json:"value"` } func main() {  _ = json.NewEncoder(os.Stdout).Encode(   []*Metric{    {"vv", 12},    {"tz", 9},    {"ss", 82},   },  ) }

输出

[{"name":"vv","value":12},{"name":"tz","value":9},{"name":"ss","value":82}] 提出问题

以上述结构体 Metric 为例,它代表的是统计指标。其中 Name 是指标名,Value 是指标值。

假设程序接收外部 Json 数据时,存在指标值为浮点数的情况,如下所示。

func main() {  var metric Metric  err := json.Unmarshal([]byte(`{"name": "tq", "value": 13.14}`), &metric)  if err != nil {   panic(err)  }  fmt.Println(metric) }

由于 Metric 结构体中定义的 Value 字段为 int64,此时对 Json 数据的反序列化将得到以下错误

panic: json: cannot unmarshal number 13.14 into Go struct field Metric.value of type int64

这种问题应该如何处理?我们能否在不改变原有结构体定义的情况下,处理这种情况。

解决方法

在 encoding/json 中,有两个非常重要的接口。

// Marshaler is the interface implemented by types that // can marshal themselves into valid JSON. type Marshaler interface {  MarshalJSON() ([]byte, error) } // Unmarshaler is the interface implemented by types // that can unmarshal a JSON description of themselves. // The input can be assumed to be a valid encoding of // a JSON value. UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. // // By convention, to approximate the behavior of Unmarshal itself, // Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op. type Unmarshaler interface {  UnmarshalJSON([]byte) error }

如果类型 T 实现了 Marshaler 或 Unmarshaler 接口方法,就能自定义了序列化和反序列化规则。

有了这个理解基础,那么对于上文中的问题,我们很自然地想到,让 Metric 结构体实现UnmarshalJSON()。

那么首先想到最简单的方式,是引入一个临时结构体:让其 Value 字段定义为 float64,其他字段维持和原结构体一致。

func (u *Metric) UnmarshalJSON(data []byte) error {  type tmp struct {   Name  string  `json:"name"`   Value float64 `json:"value"`  }  t := &tmp{   Name:  u.Name,   Value: float64(u.Value),  }  err := json.Unmarshal(data, t)  if err != nil {   return nil  }  // 注意 Unmarshaler 接口定义的以下规则:  // UnmarshalJSON must copy the JSON data  // if it wishes to retain the data after returning.  u.Name = t.Name  u.Value = int64(t.Value)  return nil }

这样做能够解决我们的问题,但并不优雅。

我们可以使用结构体的继承。这样,当结构体存在大量字段时,我们仅定义需要更改的字段即可。

func (u *Metric) UnmarshalJSON(data []byte) error {  t := &struct {   Value float64 `json:"value"`   *Metric  }{   Value:  float64(u.Value),   Metric: u,  }  if err := json.Unmarshal(data, &t); err != nil {   return err  }  u.Value = int64(t.Value)  return nil }

不过这样子会引出新的问题:继承原结构体的新 strcut 同样会继承原结构体的方法(UnmarshalJSON() 方法),这将无限循环,最终导致堆栈溢出。

fatal error: stack overflow

最佳解决方案是以结构体新类型,让新类型获得原始结构体的所有字段属性,但是却不会继承原有结构体的方法。

func (u *Metric) UnmarshalJSON(data []byte) error {  type AliasMetric Metric  t := &struct {   Value float64 `json:"value"`   *AliasMetric  }{   Value:       float64(u.Value),   AliasMetric: (*AliasMetric)(u),  }  if err := json.Unmarshal(data, &t); err != nil {   return err  }  u.Value = int64(t.Value)  return nil }

这样,就完美解决了我们的问题。

总结

在 Json 数据的处理中,我们可以通过为对象实现 Marshaler 或 Unmarshaler 接口方法,从而制定我们想要的序列化和反序列化规则。

本文通过一个简单的示例,展示了如何通过实现结构体的 UnmarshalJSON() 方法,而达到反序列化时更改字段属性的目的。

同理,我们可以为结构体定义 MarshalJSON() 方法,制定序列化规则,这就留给读者自行尝试了。

往期推荐

如何更直观地理解 Go 调度过程

如何开源自己的 Go 库

简洁的 Go 多版本管理机制

如何有效控制 Go 线程数?

1df550e9cfb5db37ca0d9051880a15cb.png

机器铃砍菜刀

欢迎添加小菜刀微信

加入Golang分享群学习交流!

感谢你的点赞和在看哦~

537651878b1080701a77c55fe46c2fba.gif



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有